package com.cloudminds.resttest.api;

import com.cloudminds.resttest.helper.AsrHelper;
import com.cloudminds.resttest.helper.FrHelper;
import com.cloudminds.resttest.ice.AiasIceClient;
import com.cloudminds.resttest.ice.AiasIceParams;
import com.cloudminds.resttest.mediatester.MediaTesterJni;
import com.cloudminds.resttest.push.PushClient;
import com.cloudminds.resttest.service.AppRTCClient;
import com.cloudminds.resttest.service.WebSocketRTCClient;
import com.cloudminds.resttest.vo.CallParams;
import com.cloudminds.resttest.webrtc.IceCandidate;
import com.cloudminds.resttest.webrtc.SessionDescription;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;


/**
 * Created by zoey on 17/4/17.oTo create WebRTC if necessary
 */

/**
 * 呼叫会话管理
 */
public class CallManager  implements  AppRTCClient.SignalingEvents,MediaTesterJni.MediaTesterEvent {
    private static final String TAG = "HS/CallManager";
    private  MediaTesterJni mediaTesterJni;
    public CallManager inst;
    private PushClient pushClient;
    public AsrHelper asrHelper;
    private FrHelper frHelper;
    public AiasIceClient aiasIceClient;
    public String rcuId;
    private String account;

    public  CallManager getInstance() {
        return inst;
    }

    private static String hsServer = "";
    private static String hsPort = "";
    private Thread heartbeatThread = new Thread(new HeartbeatRunnable());


    private static final String PCC_CREATE_ERROR = "PeerConnectionClient create fail";

    private AppRTCClient appRtcClient;
    private long callStartedTimeMs = 0;


    private AppRTCClient.AssistantConnectionParameters assistantConnectionParameters;
    private String mRemoteSDP;


    //allocated by server
    private String mSessionId;
    private boolean naviStarted;

    //    private CallKeepAliveTimer keepAliveTimer;
    public static long interruptTime=-1l;

    @Override
    public void onIceCandidate(String candidate) {

    }


    public enum CALL_STATE
    {
        PRE_STOP,      //ready to stop
        STOPPED,       // initial status
        CALLING,       // call is started
        WS_CONNECTED,  //signal is connected
        RINGING,       // call ringing on callee side
        ACCEPT,        // call accepted
        MC_CONNECTED, //media channel connected
        UNKNOWN
    }
    private CALL_STATE mCallState = CALL_STATE.STOPPED;

    //    private boolean mAcceptCandidate = false;
    private List<IceCandidate> mCachedLocalCandidates =new ArrayList<>();
    private Object candidateListLock = new Object();
    //    boolean mIsVideoEnabled = true;
    private boolean hasSdpOfferReceived = false;

    long tester = -1;

    public CallManager(){
        inst = this;
        try {
            rcuId = CallParams.generateRcuId();
            account = CallParams.getUserName();
            pushClient = new PushClient(inst);
            pushClient.connect();
            aiasIceClient = new AiasIceClient();
            asrHelper = new AsrHelper(inst, aiasIceClient);
            frHelper = new FrHelper(inst, pushClient);
           /* if (CallParams.VIDEO_ENABLE){
                 mediaTesterJni = new MediaTesterJni();
            }*/

        }catch (Exception e){

        }
    }

    /*public CallManager(boolean  flag){
        if (flag){
            CallParams.VIDEO_ENABLE =true;
            mediaTesterJni = new MediaTesterJni();
        }
    }*/

    public String getRcuId(){
        return rcuId;
    }

    public String getSessionId(){
        return mSessionId;
    }

    public String getAccount(){return  account;}

    public boolean isSignalChannelConnected(){
        boolean isConnected = false;
        if (null != appRtcClient){
            isConnected = appRtcClient.isWsConnected();
        }

        return isConnected;
    }

    public void destory() {

        closeWebSocket();
        closeWebRTC();
    }

    public void startCall(final int destroyMedia) {
        System.out.println("startCall entry");

        if (mCallState!= CALL_STATE.STOPPED && mCallState!= CALL_STATE.ACCEPT) {
            System.out.println("Ignore to start call when in calling state :"+mCallState);
            return;
        }

        channelCloseWarned = false;

        startCallInternal(destroyMedia);
    }


    private void startCallInternal(int destroyMedia){
        if (null == appRtcClient){
            appRtcClient = new WebSocketRTCClient(this);
        }

        assistantConnectionParameters = new AppRTCClient.AssistantConnectionParameters();
        assistantConnectionParameters.destroyMedia = destroyMedia;
        assistantConnectionParameters.rcuID = rcuId;
        assistantConnectionParameters.username = account;
        if (!hsServer.isEmpty())
            assistantConnectionParameters.wssUrl= "ws://"+ hsServer+":"+hsPort+"/hari";
        gDestroyMedia = 0;

        System.out.println("To connect signal server with URL<"+assistantConnectionParameters.wssUrl+">");
        mSessionId = "";
        appRtcClient.connectSignalServer(assistantConnectionParameters,mSessionId);

        mCallState = CALL_STATE.CALLING;
        naviStarted = false;
        hasSdpOfferReceived = false;

        mCachedLocalCandidates.clear();
        callStartedTimeMs = System.currentTimeMillis();

        System.out.println("startCall exit");
    }

    public void connectMediaChannel(){

        if (!isSignalChannelConnected()){
            System.out.println("Message channel is not connected, so start call!");
            /////
            return;
        }

        if (mCallState != CALL_STATE.WS_CONNECTED){
            System.out.println("Message channel is connecting, so ignored! call state:"+mCallState);
            return;
        }

        startCall(gDestroyMedia);
    }

    public boolean isCallOngoing(){
        boolean ret = true;
        if(mCallState == CALL_STATE.STOPPED || mCallState == CALL_STATE.UNKNOWN){
            ret = false;
        }
        return ret;
    }



    public void closeWebRTC(){
        System.out.println("CloseWebRTC entry");

    }

    public void closeWebSocket(){
        System.out.println("CloseWebSocket entry");
        if (appRtcClient != null) {
            appRtcClient.disconnectFromSignalServer();
            appRtcClient = null;
        }
    }




    //Below cases will entry this function:
    // 1, callstart  2, ICE reconnect 3, signal channel disconnected
    // case1: pc shall be created, offer shall be created also
    // case2: only offer shall be created to restart ICE
    // case3: none of them shall be done again
    private void handleSignalServerConnectedEvent(){
        System.out.println("On SingalServerConnected event received.");
        if(mCallState!= CALL_STATE.CALLING&&mCallState!= CALL_STATE.ACCEPT){
            System.out.println("Ignore SingalServerConnected event, because current call state is:"+mCallState);
            return;
        }

        mCallState = CALL_STATE.WS_CONNECTED;

        /*if (CallParams.FR_ENABLE){
            System.out.println("start fr thread");
            frHelper.startFr();
        }*/

        /*if (CallParams.ASR_ENABLE){
            asrHelper.startAsr();
        }*/

        /*if (CallParams.VIDEO_ENABLE){
            connectMediaServer();
        }*/

    }

    /*private void connectMediaServer() {
    }*/

    /*public String connectMediaServer(){
        boolean justCreateWebrtc = false;
        String state ="";
        System.out.println("To create WebRTC if necessary...");

        mediaTesterJni.setMediaTesterEvent(this);
        String path = new File("").getAbsolutePath();
        tester = mediaTesterJni.CreateMediaTester(true,path+"/videos/rec-sample-video.mjr");
        String sdp = mediaTesterJni.GenerateSDP(tester);
        System.out.println("local sdp:"+sdp);
        if (!sdp.isEmpty()){
            SessionDescription ssdp = new SessionDescription(SessionDescription.Type.OFFER,sdp);
            state =  appRtcClient.sendCallStart(mSessionId,ssdp);
        }
        return state;
    }*/

    /*
     * login信令服务器成功的回调
     */
    @Override
    public void onSignalServerConnected(final JSONObject data) {
        System.out.println("Connected with signal server after successful registration, where callState="+mCallState.toString());
        channelCloseWarned = false;
        if (null != data){
            try {
                mSessionId = data.getString("sid");
                if (data.has("aiasEndpoints")){
                    String aiasEndpoints = data.getString("aiasEndpoints");
                    if (aiasEndpoints!=null && !aiasEndpoints.isEmpty())
                        AiasIceParams.setEndpoints(aiasEndpoints);
                }
                if (data.has("aiasGridLocator")){
                    String aiasGridLocator = data.getString("aiasGridLocator");
                    if (aiasGridLocator!=null && !aiasGridLocator.isEmpty())
                        AiasIceParams.setGridLocator(aiasGridLocator);
                }
                aiasIceClient.forceConnect();
            }catch (JSONException e){
                e.printStackTrace();
            }
        }

        heartbeatThread.start();
        //to check whether it is reconnected
        //if(mCallState==CALL_STATE.CALLING)
        if(mCallState== CALL_STATE.ACCEPT||mCallState== CALL_STATE.CALLING)
        {
            handleSignalServerConnectedEvent();
        }

    }

    public static int gDestroyMedia = 0;



    @Override
    public void onCallSessionCreated(final String sessionId){
        System.out.println("Call Session <"+sessionId+"> is created in server side");

        mCallState = CALL_STATE.RINGING;

        String oldSessionId = mSessionId;


        if(!mSessionId.equalsIgnoreCase(oldSessionId)) {

        }

    }

    @Override
    public void onCallAccepted(SessionDescription sdp) {

    }



   /* public void onIceCandidate(final String candidate) {

        System.out.println("onIceCandidate entry with local candidate:" + candidate.toString() + " where sessionId="+mSessionId);

        // Initiator need wait callee ringing then start send update.
        IceCandidate ice = new IceCandidate("sdparta_1",0,candidate);
        synchronized (candidateListLock) {
            if (mSessionId.isEmpty()||!hasSdpOfferReceived) {
                mCachedLocalCandidates.add(ice);
            }
//                candidateListLock.notify();
        }

        if (!mSessionId.isEmpty() && hasSdpOfferReceived) {
            if (null!=appRtcClient) {
                appRtcClient.sendCallUpdate(mSessionId,ice);
            }
        }
        System.out.println("onIceCandidate exit");
    }*/



    @Override
    public void onCallUpdate(final String sessionId, final IceCandidate candidate) {
       // LogUtils.d(TAG,"onCallUpdate entry with sessionId:"+(TextUtils.isEmpty(sessionId)?"NULL":sessionId)+",remote candidate:"+candidate);
        if(!sessionId.isEmpty()){
            if(mSessionId==null || mSessionId.isEmpty() || sessionId.equalsIgnoreCase(mSessionId)){
                if(candidate==null){
                    //心跳
                }else{
                    // 存储candicate
//                    if (mPeerConnectionClient != null) {
//                        //LogUtils.d(TAG,"Add remote candidate");
//                        mPeerConnectionClient.addRemoteIceCandidate(candidate);
//                    } else {
//                        System.out.println("PeerConnectionClient is null, ignore candicate");
//                    }
                }

            } else {
                System.out.println("onCallUpdate exception: local sid is +"+mSessionId + "received sid is +"+sessionId);
            }
        }
    }

    @Override
    public void onHeartbeatResponse(String sessionId) {
        //心跳
    }

    /*@Override
    public void onCallAccepted(final SessionDescription sdp) {
        // TODO: support for non-initiator case
        mCallState = CALL_STATE.ACCEPT;
        System.out.println("onCallAccepted entry with remote SDP <"+sdp.description+">.");

        //mediaTesterJni.ProcessSDP(tester,sdp.description);
        hasSdpOfferReceived = true;

        synchronized (candidateListLock){
            if (null!=appRtcClient) {
                System.out.println("onCallSessionCreated to send all cached candidates for the sessionId="+mSessionId + " candidate num:"+mCachedLocalCandidates.size());
                for (IceCandidate candidate : mCachedLocalCandidates) {
                    appRtcClient.sendCallUpdate(mSessionId, candidate);
                }
                mCachedLocalCandidates.clear();
            }
        }
    }*/


//    public boolean isInterrupt() {
//        return isInterrupt;
//    }



    @Override
    public void onCallStop(String sessionId,String detail) {

        System.out.println("onCallStop entry with sessionId:"+sessionId);
        if(!sessionId.isEmpty()){
            if(sessionId.equalsIgnoreCase(mSessionId)){
                //close webRTC
//                closeWebSocket();
                mCallState = CALL_STATE.WS_CONNECTED;
                closeWebRTC();
            }
        }
    }

    public void sendInfo(final JSONObject data){

        if(!mSessionId.isEmpty()) {
            if (appRtcClient!=null) {
                appRtcClient.sendInfo(mSessionId,data);
            }
        }

    }

    public void stopCall(){

        System.out.println("StopCall entry");

        if(mCallState!= CALL_STATE.STOPPED) {
            System.out.println("Stop call");
            //mCallState = CALL_STATE.STOPPED;
            if (!mSessionId.isEmpty()){
                if(appRtcClient!=null){
                    //appRtcClient.sendCallStop(mSessionId);
                    appRtcClient.logout(mSessionId);
                }
            }
            closeWebSocket();
            mCallState = CALL_STATE.STOPPED;
            closeWebRTC();
        }
    }

    public void stopCall(String sid,AppRTCClient appRtcClient){
        mCallState =CALL_STATE.STOPPED;
        if (!sid.isEmpty()){
            if(appRtcClient!=null){
                appRtcClient.logout(sid);
            }
        }
        closeWebSocket();
        closeWebRTC();
        System.out.println("Stop call");
    }

    public void disconnectMediaChannel(){

        if (!mSessionId.isEmpty()){
            if(appRtcClient!=null){
                appRtcClient.sendCallStop(mSessionId);
            }
        }
        if (isSignalChannelConnected()){
            mCallState = CALL_STATE.WS_CONNECTED;
        }

        closeWebRTC();
    }



    public void sendHeartbeat(){
        appRtcClient.sendHeartbeat(mSessionId);
    }

    public void reconnectSingnalServer(){
        appRtcClient.reconnectSignalServer(assistantConnectionParameters,mSessionId);
    }


    private boolean channelCloseWarned  = false;
    private Timer timer = new Timer();
    @Override
    public void onChannelClose() {
        System.out.println("Singal channel close");
        if (mCallState == CALL_STATE.ACCEPT){
            if (!channelCloseWarned){
                channelCloseWarned = true;
            }
        }
        timer.cancel();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                startCallInternal(0);
            }
        },15*1000);
    }

    @Override
    public void onChannelConnectionError(String description) {
        System.out.println("Signal channel connect exception with message: "+description);

    }

    @Override
    public void onChannelAuthenticationError(String description) {
        if(mCallState!= CALL_STATE.STOPPED) {
            System.out.println("Signal channel authentication exception with message: "+description);

            closeWebSocket();
            closeWebRTC();
        }
    }

    @Override
    public void onChannelSignalError(String description) {
        if(mCallState!= CALL_STATE.STOPPED) {
            System.out.println("Signal message invalid exception with message: "+description);
//            //Close WebRTC
            if(description.equals("客服未接听")||description.equals("客服已挂断")){
                closeWebRTC();
            }else {

            }
        }
    }

    @Override
    public void onChannelCallRejected(String description) {
        if(mCallState!= CALL_STATE.STOPPED) {
           System.out.println("Signal channel exception with message: "+description);
            //Close WebRTC
            closeWebSocket();
            closeWebRTC();
        }
    }

    @Override
    public void onObjectIdentified(String objectName) {

    }

    @Override
    public void onFaceIdentified(String faceName) {

    }

    @Override
    public void onCommandReceived(String command) {

    }

    @Override
    public void onInfo(String type, String data) {

    }

    /*
      信令数据通道
     */
    @Override
    public JSONArray onInfo(JSONObject data) {
        JSONArray answer = new JSONArray();
        try{
            String type = data.getString("type");
           JSONObject data1 = data.getJSONObject("data");

            answer =  data1.getJSONArray("answer");

            if (type.equalsIgnoreCase("qa")){
                asrHelper.receivedAnswer();
            }
        }catch (JSONException e){
        }
        return answer;
    }

    @Override
    public void onSlamNavigationStart(final String startPoint, final String finishPoint) {

    }


    class HeartbeatRunnable implements Runnable{
        public void run(){
            while(true){
                //System.out.println("--- send heartbeat \n");
                sendHeartbeat();
                try {
                    Thread.sleep(10000);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }
}
